"use client";

import { Action, State, initState, reducer } from "@/reducers/AppReducer";
import {
  Dispatch,
  ReactNode,
  createContext,
  useContext,
  useMemo,
  useReducer,
} from "react";
// 定义一个名为state的类型 包含一个是否显示导航的属性

// 定义appcontext的参数类型 包含一个定义的状态类型 和一个更新状态的更新函数
type AppContextProps = {
  // 状态类型
  state: State;
  // 派发更新状态类型的函数
  dispatch: Dispatch<Action>;/*用于更新状态的动作类型*/
};
// 使用context Api 创建context
const AppContext = createContext<AppContextProps>(null!);

/**
 *
 * @param param0
 * !最后在导出一个方法方便外部使用这个context
 * @returns
 */

export function useAppContext() {
  return useContext(AppContext);
}

// 创建好context之后我们再创建函数组件
export default function AppContextProvider({ children }: { children: ReactNode }) {
  //接收一个children属性 从 外部嵌套子组件
  // 测试
  const [state, dispatch] = useReducer(reducer, initState) //useState<State>({displaynavigation:true,themeMode:"light"})使用useState维护状态 
  // !hook函数 作用是缓存计算结果 接受两个参数 一个是计算函数用于生成结果 ，一个是用于函数计算的依赖项 ，在初始计算以后当依赖项有改变才会进行重新计算
  const contextValue = useMemo(() => {
    return { state, dispatch }; //? 将状态和更新函数包装成对象返回 将setState 改成 dispatch
  }, [state, dispatch]); //他的依赖项就是状态和更新函数
  //* 只有当状态和更新函数发生变化时，才会重新包装对象 并赋值给contextValue 否则contextValue不会改变
  /**
   * ! 如果不使用useMemo 直接包装一个对象传递给value属性,每当 Appcontex t.Provider 组件重新渲染时都会创建一个新的包装对象传递给value属性，这样就会导致所有使用AppContext的组件都会重新渲染
   * todo 如果使用useMemo 当Appcontext.provider 重新渲染时如果状态和更新函数没有发生变化  那么contexValue也不会变 这样就不会导致使用这个context的子组件发生重新渲染
   */
  // value 属性是用来传递状态值和更新函数的
  return (
    <AppContext.Provider value={contextValue}>{children}</AppContext.Provider>
  ); //使用Appcontext.Provider作为根元素
}

// ! 将这个组件作为body元素的子元素 这样就能在所有子组件中读取里面的状态值
